package com.xxg.curd.impl;

import com.xxg.curd.CurdConfig;
import com.xxg.curd.Curd;
import com.xxg.curd.SQL;
import com.xxg.curd.SQLMap;
import com.xxg.curd.ex.CurdException;
import com.xxg.curd.ex.CurdSQLException;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class MasterCurd implements Curd {

    private final DataSource dataSource;
    private final Function<ResultSet,List<Map<String,Object>>> fun;
    private final SQL sql;
    private final SQLMap sqlMap;

    public MasterCurd(CurdConfig config){
        dataSource = config.getDataSource();
        this.sql = config.getSQL();
        fun = config.getResultFunction();
        this.sqlMap = config.getSQLMap();
    }

    @Override
    public boolean createTable(String tableName, Map<String, Object> fieldsAndType) {
        StringBuilder builderSQL = new StringBuilder();
        sql.createTable(tableName,fieldsAndType,builderSQL);
        return transaction(sql.toString());
    }

    @Override
    public boolean dropTable(String tableName) {
        StringBuilder builderSQL = new StringBuilder();
        sql.dropTable(tableName,builderSQL);
        return transaction(sql.toString());
    }

    @Override
    public boolean add(String tableName, List<Map<String, Object>> params) {
        List<StringBuilder> sqlList = new ArrayList<>();
        sql.add(tableName,params,sqlList);
        return transactionUpdate(sqlList);
    }

    @Override
    public boolean deleteById(String tableName, List<String> ids) {
        List<StringBuilder> sqlList = new ArrayList<>();
        sql.deleteById(tableName,ids,sqlList);
        return transactionUpdate(sqlList);
    }

    @Override
    public boolean updateById(String tableName, List<Map<String, Object>> params) {
        List<StringBuilder> sqlList = new ArrayList<>();
        sql.updateById(tableName,params,sqlList);
        return transactionUpdate(sqlList);
    }

    @Override
    public List<Map<String, Object>> findById(String tableName, List<String> ids) {
        StringBuilder builderSQL = new StringBuilder();
        sql.findById(tableName,ids,builderSQL);
        List<Map<String, Object>> maps = getMaps(builderSQL);
        rkv(tableName,maps);
        return maps;
    }

    public void rkv(String tableName,List<Map<String, Object>> list){
        for (Map<String, Object> map : list) {
            HashMap<String, Object> nMap = new HashMap<>(map);
            map.clear();
            for (Map.Entry<String, Object> entry : nMap.entrySet()) {
                String k = entry.getKey();
                Object v = entry.getValue();
                String a = sqlMap.getAttributeName(tableName, k);
                map.put(a,v);
            }
        }
    }

    @Override
    public List<Map<String, Object>> pageBy(String tableName, int skip, int size, Map<String, Object> where) {
        StringBuilder builderSQL = new StringBuilder();
        sql.pageBy(tableName,skip,size,where,builderSQL);
        List<Map<String, Object>> maps = getMaps(builderSQL);
        rkv(tableName,maps);
        return maps;
    }

    private List<Map<String, Object>> getMaps(StringBuilder sql) {
        String sqlText = sql.toString();
        System.out.println(sqlText);
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement();
             ResultSet res = statement.executeQuery(sqlText)){
            return fun.apply(res);
        } catch (SQLException e) {
            throw new CurdSQLException(e);
        }
    }

    private boolean transaction(String sql){
        System.out.println(sql);
        //写在一起不会自动关闭Connection
        try ( Connection connection = dataSource.getConnection();
              Statement statement = connection.createStatement()){
             statement.execute(sql);
             return true;
        } catch (SQLException e) {
            throw new CurdSQLException(e);
        }
    }

    private boolean transactionUpdate(List<StringBuilder> sqlList){
        //写在一起不会自动关闭Connection
        try (Connection connection = dataSource.getConnection();
              Statement statement = connection.createStatement()){
            connection.setAutoCommit(false);
            try{
                for (StringBuilder item : sqlList) {
                    String sql = item.toString();
                    System.out.println(sql);
                    if (statement.executeUpdate(sql) != 1) {
                        throw new CurdException("未能修改!");
                    }
                }
                connection.commit();
                return true;
            }catch (SQLException e){
                connection.rollback();
                throw e;
            }
        } catch (SQLException e) {
            throw new CurdSQLException(e);
        }
    }
}
